

// header defining the interface of the source.
#ifndef _RFID_H
#define _RFID_H

// ID-12
#define RESET_ENABLED     1
#define RESET_TIME        1000
#define RFID_REMOVED_TIME 1500
#define RFID_TAG_LENGTH   5 // 5 Bytes
#define RFID_TAG_INPUT    12 // DATA (10 ASCII) + CHECK SUM (2 ASCII

#define RESET_PIN         3
#define RFID_RX           4
#define RESET_LED_PIN     13

// if these are all non-zero, we eliminate a potential for errors
#define RFID_NORTH  10
#define RFID_SOUTH  11
#define RFID_EAST   12
#define RFID_WEST   13

#define NORTH "north"
#define SOUTH "south"
#define EAST  "east"
#define WEST  "west"

#define DEBUG 0

// Button IDS
byte rfidTagNorth[5] = {
  0x44, 0x00, 0xE7, 0x06, 0x69};
byte rfidTagSouth[5] = {
  0x44, 0x00, 0xE7, 0x06, 0x40};
byte rfidTagEast[5] = {
  0x44, 0x00, 0xE6, 0xC8, 0xA9};
byte rfidTagWest[5] = {
  0x44, 0x00, 0xE6, 0x9C, 0x5C};
  


NewSoftSerial rfidSerial(RFID_RX, -1);

class RFID {
public:

  void init() {
    pinMode(RESET_PIN, OUTPUT);
    rfidSerial.begin(9600);
    // clear current tag
    clearTag(rfidTagCurrent, RFID_TAG_LENGTH);

    nowReset = 0;
    nowLastRfid = 0;
    rfidEnabled = false;
    rfidTagSeen = false;
  }

  const unsigned char readTagIfAvailable() {
    byte action = 0;
    unsigned int now = millis();

    updateID12(false);
    clearTag(rfidTagTemp, 6);

    // serial connection with rfid reader
    if (rfidSerial.available()) {

      // wait for the next STX byte
      while(rfidSerial.available() && action != 0x02)
        action = rfidSerial.read();

      // STX byte found -> RFID tag available
      if(action == 0x02)
      {
        if(readID12(rfidTagTemp))
        {
          nowLastRfid = millis();
          rfidTagSeen = true;
          updateCurrentRfidTag(rfidTagTemp);
        }
      }

    }
    else if(rfidEnabled && rfidTagSeen && (now - nowLastRfid) >= RFID_REMOVED_TIME)
    {    
      rfidTagSeen = false;
      updateCurrentRfidTag(rfidTagTemp);
    }

    unsigned char result = NULL;

    if ( equals(rfidTagNorth, rfidTagCurrent) ) {
      result = RFID_NORTH;
    }
    if ( equals(rfidTagSouth, rfidTagCurrent) ) {
      result = RFID_SOUTH;
    }
    if ( equals(rfidTagEast, rfidTagCurrent) ) {
      result = RFID_EAST;
    }
    if ( equals(rfidTagWest, rfidTagCurrent) ) {
      result = RFID_WEST;
    }  

    clearTag(rfidTagCurrent, RFID_TAG_LENGTH);
    return result;
  }

  void clearTag(byte *arr, byte len)
  {
    byte i;
    for (i=0; i < len ;i++) arr[i] = 0;
  }

  // Save RFID Tag
  void saveTag(byte *tagIn, byte *tagOut)
  {
    byte i;
    for (i=0; i < RFID_TAG_LENGTH ;i++) tagOut[i] = tagIn[i];
  }

  // Compare two RFID tags
  boolean equals(byte *tag1, byte *tag2)
  {
    boolean result = false;
    byte j;

    for (j=0; j < RFID_TAG_LENGTH ;j++) 
    {
      if(tag1[j] != tag2[j]) break;
      else if (j == RFID_TAG_LENGTH-1) result = true;
    }    
    return result;
  }

private:
  // last seen tag
  byte rfidTagCurrent[RFID_TAG_LENGTH];

  // millis of last Reset 
  unsigned int nowReset;
  // millis of last seen rfid tag
  unsigned int nowLastRfid;
  // reader is in reset state
  boolean rfidEnabled;
  // 
  boolean rfidTagSeen;

  // temp tag
  byte rfidTagTemp[6];

  // TODO: Do we need this function in the long run?
  // print actual RFID tag number to serial
  void updateCurrentRfidTag(byte *tagNew)
  {  
    // only print changed value     
    if(!equals(tagNew, rfidTagCurrent)) {

      saveTag(tagNew, rfidTagCurrent);
      byte i = 0;

      // STX
      #if DEBUG == 1
      Serial.print(0x02, BYTE);

      for (i=0; i<5; i++) {
        if (rfidTagCurrent[i] < 16) Serial.print("0");
          Serial.print(rfidTagCurrent[i], HEX);
      }

      // ETX
      Serial.print(0x03, BYTE);
      Serial.println();
      #endif
    }
  }

  // read data from ID-12 reader
  boolean readID12(byte *code)
  {
    boolean result = false;
    byte val = 0;
    byte bytesIn = 0;
    byte tempbyte = 0;
    byte checksum = 0;

    // read 10 digit code + 2 digit checksum
    while (bytesIn < RFID_TAG_INPUT) {                        
      if( rfidSerial.available() > 0) { 
        val = rfidSerial.read();

        // if CR, LF, ETX or STX before the 10 digit reading -> stop reading
        if((val == 0x0D)||(val == 0x0A)||(val == 0x03)||(val == 0x02)) break;

        // Do Ascii/Hex conversion:
        if ((val >= '0') && (val <= '9')) 
          val = val - '0';
        else if ((val >= 'A') && (val <= 'F'))
          val = 10 + val - 'A';


        // Every two hex-digits, add byte to code:
        if (bytesIn & 1 == 1) {
          // make some space for this hex-digit by
          // shifting the previous hex-digit with 4 bits to the left:
          code[bytesIn >> 1] = (val | (tempbyte << 4));

          // If we're at the checksum byte, Calculate the checksum... (XOR)
          if (bytesIn >> 1 != RFID_TAG_LENGTH) checksum ^= code[bytesIn >> 1]; 
        } 
        else {
          // Store the first hex digit first...
          tempbyte = val;                           
        }

        // ready to read next digit
        bytesIn++;                                
      } 
    }

    // read complete
    if (bytesIn == RFID_TAG_INPUT) { 
      // valid tag
      if(code[5] == checksum) result = true; 
    }

    // reset id-12
    updateID12(true);

    return result;
  }


  // Update reset state of the RFID reader
  void updateID12(boolean reset_)
  {
    // reset is disabled
    if(RESET_ENABLED == 0 && rfidEnabled == true) return;

    // don't reset, just check if the id-12 should be enabled again 
    if(reset_ == false)
    {
      // current time
      unsigned int now = millis();

      // id-12 is disabled and ( reset period is over or initial id-12 startup )
      if (rfidEnabled == false && ((now - nowReset) >= RESET_TIME || nowReset == 0)) 
      { 
        digitalWrite(RESET_LED_PIN, LOW);
        digitalWrite(RESET_PIN, HIGH);
        rfidEnabled = true;
      }
    }
    // reset rfid reader
    else
    {
      digitalWrite(RESET_LED_PIN, HIGH);
      digitalWrite(RESET_PIN, LOW);

      nowReset = millis();
      rfidEnabled = false;  
    }

  }


};

static RFID Rfid;

#endif // _RFID_H










